package com.evan.review.classloader;

import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.ScrollView;
import android.widget.TextView;

import androidx.appcompat.app.AppCompatActivity;

import com.evan.review.R;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Method;

import dalvik.system.DexClassLoader;

/**
 * 类加载过程详细演示Activity
 * <p>
 * 特点：
 * 1. 详细展示类的加载过程：加载、连接(验证、准备、解析)、初始化
 * 2. 演示破坏双亲委派机制的案例及风险
 * 3. 演示动态加载外部dex文件的过程
 * 4. 演示类加载器在热修复和插件化中的应用
 */
public class ClassLoadingDetailActivity extends AppCompatActivity {

    private static final String TAG = "ClassLoadingDetail";
    private TextView tvContent;
    private ScrollView scrollView;
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_class_loading_detail);
        
        initViews();
        showClassLoadingProcess();
    }
    
    private void initViews() {
        tvContent = findViewById(R.id.tv_content);
        scrollView = findViewById(R.id.scroll_view);
        
        // 类加载过程按钮
        Button btnLoadingProcess = findViewById(R.id.btn_loading_process);
        btnLoadingProcess.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                showClassLoadingProcess();
            }
        });
        
        // 破坏双亲委派按钮
        Button btnBreakDelegation = findViewById(R.id.btn_break_delegation);
        btnBreakDelegation.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                demonstrateBreakingDelegation();
            }
        });
        
        // 动态加载外部类按钮
        Button btnDynamicLoading = findViewById(R.id.btn_dynamic_loading);
        btnDynamicLoading.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                demonstrateDynamicLoading();
            }
        });
        
        // 热修复原理按钮
        Button btnHotfix = findViewById(R.id.btn_hotfix);
        btnHotfix.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                explainHotfixPrinciple();
            }
        });
    }
    
    /**
     * 详细展示类加载过程
     */
    private void showClassLoadingProcess() {
        StringBuilder content = new StringBuilder();
        content.append("【类加载过程详解】\n\n");
        
        content.append("类的生命周期有五个阶段：\n");
        content.append("1. 加载（Loading）\n");
        content.append("2. 验证（Verification）\n");
        content.append("3. 准备（Preparation）\n");
        content.append("4. 解析（Resolution）\n");
        content.append("5. 初始化（Initialization）\n\n");
        
        content.append("一、加载阶段\n");
        content.append("- 通过类的全限定名获取二进制字节流\n");
        content.append("- 将字节流所代表的静态存储结构转为方法区运行时数据结构\n");
        content.append("- 在内存中生成代表这个类的java.lang.Class对象\n");
        content.append("- Android中通过DexClassLoader/PathClassLoader加载dex文件\n\n");
        
        content.append("二、验证阶段\n");
        content.append("- 确保Class文件符合规范，不会危害虚拟机安全\n");
        content.append("- 包括文件格式验证、元数据验证、字节码验证、符号引用验证\n");
        content.append("- Android中，编译时ProGuard和D8/R8也进行部分验证\n\n");
        
        content.append("三、准备阶段\n");
        content.append("- 为类变量（静态变量）分配内存并设置初始值\n");
        content.append("- 初始值通常是数据类型的零值，如int为0，引用为null\n");
        content.append("- static final修饰的常量在此阶段直接赋为指定值\n");
        content.append("- 示例：static int value = 123; // 此时value为0，而非123\n\n");
        
        content.append("四、解析阶段\n");
        content.append("- 将常量池内的符号引用替换为直接引用\n");
        content.append("- 包括类、接口、字段、方法的解析\n");
        content.append("- 可能在初始化之后再开始，具体取决于虚拟机实现\n\n");
        
        content.append("五、初始化阶段\n");
        content.append("- 执行类构造器<clinit>()方法\n");
        content.append("- <clinit>由所有类变量赋值操作和静态代码块合并而成\n");
        content.append("- 顺序由源文件中出现的顺序决定\n");
        content.append("- 示例中静态变量：此时value才被赋值为123\n");
        content.append("- 父类优先于子类初始化\n\n");
        
        content.append("类初始化的触发时机：\n");
        content.append("1. 创建类实例（new、反射、克隆、反序列化）\n");
        content.append("2. 访问类的静态变量或静态方法（final常量除外）\n");
        content.append("3. 调用Class.forName(\"类名\")\n");
        content.append("4. 子类初始化时，父类会先初始化\n");
        content.append("5. 包含main方法的类启动时\n\n");
        
        content.append("不会初始化的情况：\n");
        content.append("1. 通过子类引用父类的静态字段，只初始化父类\n");
        content.append("2. 定义对象数组，不会触发类初始化\n");
        content.append("3. 常量（static final）不会触发类初始化\n");
        content.append("4. 通过ClassLoader.loadClass加载类\n\n");
        
        content.append("类在使用前必须完成加载、验证、准备、解析和初始化这5个阶段，它们严格按这个顺序执行。\n\n");
        
        content.append("面试要点：\n");
        content.append("- 区分类加载的5个阶段及其作用\n");
        content.append("- 理解static变量和static final变量在准备阶段的不同处理\n");
        content.append("- 掌握类初始化的触发条件\n");
        content.append("- 理解常见框架（如Spring）中类加载的应用\n");
        
        tvContent.setText(content.toString());
        scrollToTop();
    }
    
    /**
     * 演示破坏双亲委派机制
     */
    private void demonstrateBreakingDelegation() {
        StringBuilder content = new StringBuilder();
        content.append("【破坏双亲委派机制】\n\n");
        
        content.append("一、双亲委派模型正常工作流程\n");
        content.append("1. 收到类加载请求时，先委托父加载器加载\n");
        content.append("2. 父加载器无法加载时，子加载器才自己尝试加载\n");
        content.append("3. 自底向上请求，自顶向下加载\n\n");
        
        content.append("二、破坏双亲委派的场景\n");
        content.append("1. JDK1.2之前，自定义ClassLoader需重写loadClass()\n");
        content.append("2. 线程上下文类加载器（TCCL）用于SPI机制\n");
        content.append("   - 如JDBC、JNDI等接口由rt.jar提供\n");
        content.append("   - 实现类由应用ClassLoader加载\n");
        content.append("   - 需要通过TCCL打破双亲委派访问实现类\n");
        content.append("3. OSGi模块化系统\n");
        content.append("   - 允许同一个类由不同类加载器在不同模块加载\n\n");
        
        content.append("三、Android中的破坏双亲委派\n");
        content.append("1. 插件化和热修复技术\n");
        content.append("   - 需要自定义ClassLoader加载新版本类\n");
        content.append("   - 例如Tinker热修复对原始类加载过程的干预\n");
        content.append("2. 多dex加载机制\n");
        content.append("   - Android应用65535方法数限制的解决方案\n");
        content.append("   - ClassLoader加载主dex和次级dex文件\n\n");
        
        content.append("四、破坏双亲委派的实现方式\n");
        content.append("1. 重写loadClass()方法（不推荐）\n");
        content.append("```java\n@Override\nprotected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {\n    // 尝试自己加载，不委托父加载器\n    Class<?> c = findClass(name);\n    if (c == null) {\n        // 找不到才委托父加载器\n        return super.loadClass(name, resolve);\n    }\n    return c;\n}\n```\n\n");
        
        content.append("2. 使用上下文类加载器（合理做法）\n");
        content.append("```java\nClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();\nClass<?> aClass = contextClassLoader.loadClass(\"com.example.MyClass\");\n```\n\n");
        
        content.append("五、风险和注意事项\n");
        content.append("1. 破坏双亲委派会导致类命名空间混乱\n");
        content.append("2. 可能导致同一个类有多个不同版本同时加载\n");
        content.append("3. 引起类型转换异常：ClassCastException\n");
        content.append("4. 安全风险：可能加载恶意类替换核心类库\n\n");
        
        content.append("面试要点：\n");
        content.append("- 理解破坏双亲委派的合理场景和必要性\n");
        content.append("- 能解释Android热修复中如何正确破坏双亲委派\n");
        content.append("- 掌握Thread Context ClassLoader的作用\n");
        content.append("- 知道破坏双亲委派的潜在风险\n");
        
        tvContent.setText(content.toString());
        scrollToTop();
    }
    
    /**
     * 演示动态加载外部dex
     */
    private void demonstrateDynamicLoading() {
        StringBuilder content = new StringBuilder();
        content.append("【动态加载外部类演示】\n\n");
        
        try {
            content.append("一、动态加载原理\n");
            content.append("在Android中，DexClassLoader可以加载未安装APK/JAR/DEX文件中的类。这是实现动态加载和插件化的基础。\n\n");
            
            content.append("二、动态加载外部类的步骤\n");
            content.append("1. 获取外部dex文件（可从网络下载或从assets复制）\n");
            content.append("2. 创建优化目录（optimizedDirectory）\n");
            content.append("3. 构造DexClassLoader实例\n");
            content.append("4. 通过反射调用外部类的方法\n\n");
            
            content.append("三、模拟外部dex加载过程\n");
            content.append("由于无法在模拟器中演示完整过程，我们模拟dex加载流程：\n\n");
            
            content.append("1. 创建DexClassLoader:\n");
            String dexPath = getPackageCodePath();  // 使用当前应用路径模拟
            File optimizedDir = getCodeCacheDir();
            content.append("   - DEX路径: ").append(dexPath).append("\n");
            content.append("   - 优化目录: ").append(optimizedDir.getAbsolutePath()).append("\n\n");
            
            DexClassLoader dexClassLoader = new DexClassLoader(
                    dexPath,
                    optimizedDir.getAbsolutePath(),
                    null,
                    getClassLoader()
            );
            
            content.append("2. 加载并反射调用当前应用中的类:\n");
            Class<?> testClass = dexClassLoader.loadClass("com.evan.review.classloader.TestInitClass");
            Object instance = testClass.newInstance();
            Method method = testClass.getMethod("instanceMethod");
            method.invoke(instance);
            content.append("   - 成功加载和调用TestInitClass类\n");
            content.append("   - 请查看Logcat日志以验证方法调用\n\n");
            
            content.append("四、真实场景应用\n");
            content.append("1. 插件化框架(如DroidPlugin, VirtualAPK)\n");
            content.append("2. 动态更新组件而不发布新版本\n");
            content.append("3. 动态加载主题资源或多语言包\n");
            content.append("4. 减小APK体积，按需加载功能模块\n\n");
            
            content.append("五、Android P及以上的限制\n");
            content.append("从Android P开始，对非安装应用的直接访问有限制：\n");
            content.append("1. 需在manifest中显式声明非SDK接口\n");
            content.append("2. 部分私有API的使用受限\n");
            content.append("3. 解决方法：使用App Bundle或Dynamic Feature\n\n");
            
            content.append("六、注意事项\n");
            content.append("1. Context资源访问问题\n");
            content.append("2. 四大组件需特殊处理才能动态注册\n");
            content.append("3. 类版本冲突和兼容性问题\n");
            content.append("4. 安全风险：确保外部dex文件签名验证\n\n");
            
            content.append("面试要点：\n");
            content.append("- 熟悉DexClassLoader的构造函数参数及意义\n");
            content.append("- 理解插件化技术的原理和实现方式\n");
            content.append("- 掌握Android不同版本对动态加载的限制变化\n");
            content.append("- 能解释资源加载和类加载的区别\n");
            
        } catch (Exception e) {
            content.append("演示出错：\n").append(e.getMessage());
            Log.e(TAG, "演示动态加载出错", e);
        }
        
        tvContent.setText(content.toString());
        scrollToTop();
    }
    
    /**
     * 解释热修复原理
     */
    private void explainHotfixPrinciple() {
        StringBuilder content = new StringBuilder();
        content.append("【热修复原理详解】\n\n");
        
        content.append("一、热修复基本原理\n");
        content.append("热修复技术允许在不发布新版本的情况下修复线上bug。核心原理是利用ClassLoader加载修复后的类替代有bug的类。\n\n");
        
        content.append("二、主流热修复方案\n");
        content.append("1. 类加载方案(如Tinker、QFix)\n");
        content.append("2. 底层替换方案(如AndFix)\n");
        content.append("3. 插桩方案(如Robust)\n\n");
        
        content.append("三、基于类加载的热修复原理\n");
        content.append("基于类加载方案借助ClassLoader特性：\n");
        content.append("1. 一个类由哪个ClassLoader加载是确定的\n");
        content.append("2. 类加载具有缓存机制：已加载的类不会重新加载\n");
        content.append("3. 要替换有bug的类，必须在类首次加载前完成\n\n");
        
        content.append("四、Tinker热修复实现原理\n");
        content.append("Tinker通过以下步骤实现热修复：\n");
        content.append("1. 基线dex与补丁dex合并生成新dex\n");
        content.append("2. 创建自定义DexClassLoader加载新dex\n");
        content.append("3. 通过反射修改系统DexPathList，调整dex加载顺序\n");
        content.append("4. 确保补丁类优先于原类加载\n\n");
        
        content.append("五、类加载优先级控制关键代码（模拟）\n");
        content.append("```java\n// 获取DexPathList对象\nField pathListField = BaseDexClassLoader.class.getDeclaredField(\"pathList\");\npathListField.setAccessible(true);\nObject pathList = pathListField.get(originalClassLoader);\n\n// 获取dexElements数组\nField dexElementsField = pathList.getClass().getDeclaredField(\"dexElements\");\ndexElementsField.setAccessible(true);\nObject[] originalElements = (Object[]) dexElementsField.get(pathList);\n\n// 创建新的dexElements数组，将补丁元素放在前面\nObject[] patchElements = ... // 补丁中的元素\nObject[] newElements = 合并(patchElements, originalElements);\n\n// 替换原始dexElements\ndexElementsField.set(pathList, newElements);\n```\n\n");
        
        content.append("六、热修复技术局限性\n");
        content.append("1. 类替换限制：只能修复方法实现，不能变更类结构\n");
        content.append("2. 资源替换：需要特殊的资源修复方案\n");
        content.append("3. SO库替换：不同厂商实现差异大\n");
        content.append("4. 兼容性问题：不同Android版本差异\n");
        content.append("5. 多ClassLoader场景处理复杂\n\n");
        
        content.append("七、Android Q及以上限制\n");
        content.append("随着Android版本升级，非公开API访问限制越来越严格：\n");
        content.append("1. Android Q开始对非SDK接口更严格限制\n");
        content.append("2. 对DexClassLoader/PathClassLoader的反射访问受限\n");
        content.append("3. 解决方案：\n");
        content.append("   - 使用AndroidX App Bundle动态更新\n");
        content.append("   - 使用Google Play应用内更新API\n");
        content.append("   - 依赖厂商提供的热更新服务\n\n");
        
        content.append("面试要点：\n");
        content.append("- 能清晰解释各种热修复方案的优缺点\n");
        content.append("- 理解类加载时机对热修复的影响\n");
        content.append("- 掌握ClassLoader委派机制在热修复中的应用\n");
        content.append("- 知道Android各版本对热修复限制的变化\n");
        content.append("- 能解释用户重启应用与热修复应用场景差异\n");
        
        tvContent.setText(content.toString());
        scrollToTop();
    }
    
    /**
     * 滚动到页面顶部
     */
    private void scrollToTop() {
        scrollView.post(new Runnable() {
            @Override
            public void run() {
                scrollView.scrollTo(0, 0);
            }
        });
    }
} 